﻿using Fuse8.ContentManagementFramework.Administration.Logic;
using Fuse8.ContentManagementFramework.Administration.Models;
using Fuse8.ContentManagementFramework.BizFacade.Services;
using Fuse8.ContentManagementFramework.BizFacade.Workflow;
using Fuse8.ContentManagementFramework.Domain;
using Fuse8.ContentManagementFramework.Domain.Special;
using Fuse8.ContentManagementFramework.SystemFacade.Common;
using Fuse8.ContentManagementFramework.Web.ApplicationRuntime;
using Fuse8.ContentManagementFramework.Web.Etc;
using Fuse8.DomainFramework.Common.Locator;
using Fuse8.DomainFramework.Domain;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;

namespace Fuse8.ContentManagementFramework.Administration.Controllers
{
    public class WorkflowEventArgs<T> : EventArgs
    {
        public T Entity { get; internal set; }

        public WorkflowEventArgs()
            : base()
        { }
    }

    public abstract class CmsAdminWorkflowController<T> : CmsAdminController<T>
        where T : CmsRevisionableEntityObject, new()
    {
        #region Fields

        private CmsWorkflowService _workflowService;
        private CmsUserService _userService;
        private CmsEntityAnnotationService _entityAnnotationService;

        #endregion Fields

        #region Events

        public event EventHandler<WorkflowEventArgs<T>> WorkflowProcessed;

        #endregion Events

        #region Raise Events

        /// <summary>
        /// Raises the <see cref="Validating"/> event before validates data.
        /// </summary>
        /// <remarks>
        /// The OnValidating() method also enables derived classes to handle the event without attaching a delegate.
        /// This is the preferred technique for handling the event in a derived class.
        /// </remarks>
        protected virtual void OnWorkflowProcessed(WorkflowEventArgs<T> e)
        {
            EventHandler<WorkflowEventArgs<T>> workflowProcessedHandler = WorkflowProcessed;

            if (workflowProcessedHandler != null)
            {
                workflowProcessedHandler(this, e);
            }
        }

        #endregion Raise Events

        #region Properties

        protected virtual CmsWorkflowService WorkflowService
        {
            get
            {
                if (_workflowService == null)
                {
                    _workflowService = new CmsWorkflowService();
                }

                return _workflowService;
            }
        }

        protected virtual CmsUserService UserService
        {
            get
            {
                if (_userService == null)
                {
                    _userService = new CmsUserService();
                }

                return _userService;
            }
        }

        protected virtual CmsEntityAnnotationService EntityAnnotationService
        {
            get
            {
                if (_entityAnnotationService == null)
                {
                    _entityAnnotationService = new CmsEntityAnnotationService();
                }

                return _entityAnnotationService;
            }
        }

        protected abstract CmsEntityRevisionService<T> EntityRevisionService
        {
            get;
        }

        #endregion Properties

        #region Revision history

        public ActionResult RevisionHistory(int id)
        {
            var data = PopulateRevisionModel(id);

            return PartialView(data);
        }

        protected virtual IEnumerable<CmsAdminRevisionViewModel> PopulateRevisionModel(int entityId)
        {
            throw new NotImplementedException();
        }

        #endregion Revision history

        #region Workflow methods

        public ActionResult WorkflowNotes(int id)
        {
            var data = PopulateWorkflowNotesModel(id);

            return PartialView(data);
        }

        protected virtual IEnumerable<CmsWorkflow> PopulateWorkflowNotesModel(int entityId)
        {
            throw new NotImplementedException();
        }

        public virtual ActionResult SendForApproval(int id)
        {
            var currentEntity = EntityRevisionService.GetCurrentRevision(id);

            using (var provider = new CmsWorkflowFacade<T, int>(currentEntity))
            {
                return PartialView(
                    new CmsAdminWorkflowSendForApprovalModel(UserService.GetForUserGroup(provider.CurrentState.UserGroupId))
                    {
                        EntityId = currentEntity.Id,
                        WorkflowTransitionId = provider.GetAvailableTransitions().Any() ?
                            provider.GetAvailableTransitions().First().Key :
                            0
                    });
            }
        }

        public virtual ActionResult ReassignForApproval(int? userGroupId, int? assignedUserId)
        {
            return PartialView(
                new CmsAdminWorkflowReassignForApprovalModel(UserService.GetForUserGroup(userGroupId))
                {
                    AssignedUserId = assignedUserId
                });
        }

        public virtual ActionResult WorkflowItem(int id)
        {
            ActionResult result = null;

            if (id > 0)
            {
                var allActiveItems = EntityRevisionService.FindAllActiveRevisions();

                if (allActiveItems != null)
                {
                    var currentItem = allActiveItems.FirstOrDefault(p => p.Id == id);

                    if (currentItem != null)
                    {
                        Dictionary<int, string> transitions = null;

                        using (var provider = new CmsWorkflowFacade<T, int>(currentItem))
                        {
                            transitions = provider.GetAvailableTransitions();
                        }

                        result = View("/admin/views/CmsWorkflow/WorkflowItem.cshtml",
                            new CmsAdminWorkflowActiveModel
                            {
                                Id = currentItem.Id,
                                EntityType = currentItem.TypeParameter,
                                AvailableTransitions = transitions ?? new Dictionary<int, string>(),
                                Title = currentItem.WorkflowTitle,
                                ApprovalDate = currentItem.Workflow.ApproveDateDeadline,
                                Cycle = currentItem.Workflow.Cycle,
                                State = currentItem.Workflow.LatestWorkflowState.WorkflowState,
                                Status = currentItem.Workflow.LatestWorkflowState.WorkflowState.ToString(),
                                UserGroupId = currentItem.Workflow.LatestWorkflowState.UserGroupId,
                                UserId = currentItem.Workflow.LatestWorkflowState.UserId,
                                UserGroup = currentItem.Workflow.LatestWorkflowState.AssignedUserGroup,
                                User = currentItem.Workflow.LatestWorkflowState.AssignedUser
                            });
                    }
                }
            }

            return result ?? RedirectToAction("Index", "CmsWorkflow");
        }

        [HttpPost]
        public virtual ActionResult WorkflowItem(int id, int workflowTransitionId, string notes, int? assignedUserId = null)
        {
            ActionResult result = null;

            var entity = EntityRevisionService.GetCurrentRevision(id);

            if (entity != null)
            {
                try
                {
                    ProcessWorkflow(entity, notes, workflowTransitionId, assignedUserId);

                    CmsRoute.ClearByDependency();

                    result = Json(1);
                }
                catch (Exception ex)
                {
                    ControllerContext.HttpContext.Response.StatusCode = 500;
                    result = Content(ex.Message, "text/html", Encoding.UTF8);
                }
            }

            return result ?? Json(0);
        }

        protected virtual ActionResult StartWorkflow(T entity, DateTime? approvalDeadlineDate, DateTime? reviewDeadlineDate)
        {
            return StartWorkflow(entity, approvalDeadlineDate, reviewDeadlineDate, 0, string.Empty);
        }

        protected virtual ActionResult StartWorkflow(T entity, DateTime? approvalDeadlineDate, DateTime? reviewDeadlineDate, int transitionId, string notes, int? assignedUserId = null)
        {
            EntityRevisionService.StartWorkflow(entity, approvalDeadlineDate, reviewDeadlineDate, transitionId, notes, assignedUserId);

            return RedirectToAction("Index", "CmsWorkflow");
        }

        protected virtual void ProcessWorkflow(T entity, string notes, int transitionId = 0, int? assignedUserId = null)
        {
            EntityRevisionService.ProcessWorkflow(entity, notes, transitionId, assignedUserId);

            if (transitionId > 0)
            {
                OnWorkflowProcessed(new WorkflowEventArgs<T> { Entity = entity });
            }
        }

        #endregion Workflow methods

        #region Annotations

        public virtual ActionResult EntityAnnotations(int id)
        {
            throw new NotImplementedException();
        }

        [NonAction]
        protected virtual ActionResult EntityAnnotations(int id, string entityTypeParameter)
        {
            var allUserGroups = MembershipService.GetAllUserGroups();

            var meta = EntityMetaInformationService.GetMetaForEntity(new CmsUserGroup());

            var model = new CmsAdminAnnotationModel
            {
                EntityId = id,
                EntityTypeParameter = entityTypeParameter,
                CurrentUser = CmsContext.ContextUser,
                ColorDefinitions = meta != null ?
                    meta.Metas.Select(
                        p => new CmsAdminAnnotationColorScheme
                        {
                            Color = p.EntityMetaValue,
                            UserGroupId = p.EntityId,
                            UserGroupName = (allUserGroups.FirstOrDefault(q => q.Id == p.EntityId) ?? new CmsUserGroup()).GroupName
                        }) :
                    allUserGroups.Select(
                        p => new CmsAdminAnnotationColorScheme
                        {
                            Color = CmsUserGroup.DefaultUserGroupColor,
                            UserGroupId = p.Id,
                            UserGroupName = p.GroupName
                        }
                    )
            };

            var coloredUserGroups = model.ColorDefinitions.Select(p => p.UserGroupId);

            var data = EntityAnnotationService.GetForEntity(id, entityTypeParameter); //TO DO: remove hardcode

            if (data != null)
            {
                model.Notes = data.Select(
                    p => new CmsEntityAnnotationModel
                    {
                        AnnotationText = p.AnnotationText,
                        EntityId = p.EntityId,
                        EntityTypeParameter = p.EntityTypeParameter,
                        Id = p.Id,
                        UserGroupId = p.UserGroups.FirstOrDefault(q => coloredUserGroups.Contains(q.Id)).Id,
                        IsMinimized = p.IsMinimized,
                        X = p.X,
                        Y = p.Y,
                        Z = p.Z,
                        Comments = p.Comments != null ?
                            p.Comments.OrderBy(q => q.OrderIndex).AsEnumerable() :
                            new List<CmsEntityAnnotationComment>(),
                        UserCreatedBy = p.UserCreatedBy ?? "unknown"
                    });
            }
            else
            {
                model.Notes = new List<CmsEntityAnnotationModel>();
            }

            var currentUserGroups = MembershipService.GetUserGroups(ContextUser);
            if ((currentUserGroups != null) && (currentUserGroups.Any()))
            {
                model.CurrentUserGroupId = currentUserGroups.FirstOrDefault(q => coloredUserGroups.Contains(q.Id)).Id;
            }

            return PartialView(model);
        }

        public virtual ActionResult SaveEntityAnnotations(CmsAdminAnnotationSaveModel model)
        {
            ActionResult result = null;

            if (ModelState.IsValid)
            {
                EntityAnnotationService.SaveForEntity(
                    model.EntityId,
                    model.EntityTypeParameter, //TO DO: remove hardcode
                    (model.Notes != null) && model.Notes.Any() ?
                        model.Notes.Select(
                            p => new CmsEntityAnnotation
                            {
                                Id = p.Id,
                                AnnotationText = p.AnnotationText,
                                EntityId = model.EntityId,
                                EntityTypeParameter = model.EntityTypeParameter, //TO DO: remove hardcode
                                IsMinimized = p.IsMinimized,
                                X = p.X,
                                Y = p.Y,
                                Z = p.Z,
                                Comments = p.Comments != null && p.Comments.Any() ?
                                    p.Comments.Select(
                                        q => new CmsEntityAnnotationComment
                                        {
                                            Id = q.Id,
                                            AnnotationId = q.AnnotationId,
                                            CommentText = q.CommentText
                                        }) :
                                    null
                            }) :
                        null,
                        (model.AnnotationsForDelete != null) && model.AnnotationsForDelete.Any() ?
                            model.AnnotationsForDelete.Where(p => p > 0) :
                            null,
                        (model.CommentsForDelete != null) && model.CommentsForDelete.Any() ?
                            model.CommentsForDelete.Where(p => p > 0) :
                            null
                        );

                result = Json(1);
            }

            return result ?? Json(0);
        }

        #endregion Annotations
    }

    public abstract class CmsAdminController<T> : CmsAdminController
        where T : class, IEntityObject
    {
        private IStorage<T> _storage = new StorageFacade<T>();

        public virtual IStorage<T> CommonStorage
        {
            get
            {
                return _storage;
            }
        }

        #region FileView

        public virtual ActionResult FileSystemView(int? id)
        {
            ActionResult result = new EmptyResult();

            var data = FileViewFactory.GetRoot<T>(id);

            if (data != null)
            {
                result = PartialView(data);
            }

            return result;
        }

        #endregion FileView

        #region Category

        [HttpPost]
        public virtual ActionResult AddCategory(int id, string categoryName)
        {
            return AddOrEditCategory(null, categoryName);
        }

        [HttpPost]
        public virtual ActionResult EditCategory(int id, string categoryName)
        {
            return AddOrEditCategory(id, categoryName);
        }

        private ActionResult AddOrEditCategory(int? id, string categoryName)
        {
            ActionResult result = null;

            if (!string.IsNullOrWhiteSpace(categoryName))
            {
                try
                {
                    result = Json(
                        (!id.HasValue ?
                            _AddCategory(categoryName) :
                            _EditCategory(id.Value, categoryName)) ? 1 : 0);
                }
                catch (Exception ex)
                {
                    ControllerContext.HttpContext.Response.StatusCode = 500;
                    result = Content(ex.Message, "text/html", Encoding.UTF8);
                }
            }

            return result ?? Json(0);
        }

        protected virtual bool _AddCategory(string categoryName)
        {
            throw new NotImplementedException();
        }

        protected virtual bool _EditCategory(int id, string categoryName)
        {
            throw new NotImplementedException();
        }

        #endregion Category
    }
}